#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include "display_manager.h"
#include <stdio.h>
#include <string.h>
#include "config.h"

#define FB_DEVICE_NAME "/dev/fb0"

static int FBDeviceInit(void);
static int FBShowPixel(int x, int y, unsigned int clor);
static int FBCleanScreen(unsigned int clor);
static int g_dwLineWidth;
static int g_dwPixelWidth;
static struct fb_var_screeninfo g_tFBVar;
static unsigned char *g_pucFBMem;
static int g_dwScreenSize;
T_DisOpr g_tFBOpr = {
    .name = "fb",
    .DeviceInit = FBDeviceInit,
    .ShowPixel = FBShowPixel,
    .CleanScreen = FBCleanScreen,
};

static int FBDeviceInit(void)
{
    int ret;

    int fd = open(FB_DEVICE_NAME, O_RDWR);
    if (fd==-1)
    {
        DBG_PRINTF("chen FB_DEVICE_NAME open error \n");
    }
    ret = ioctl(fd, FBIOGET_VSCREENINFO, &g_tFBVar);
    if (ret)
    {
        DBG_PRINTF("chen ioctl  error \n");
    }
    g_dwScreenSize = g_tFBVar.xres * g_tFBVar.yres * g_tFBVar.bits_per_pixel / 8;
    g_pucFBMem = mmap(NULL, g_dwScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (g_pucFBMem == (unsigned char *)-1)
    {
        DBG_PRINTF("chen fb mmap  error \n");
    }
    g_tFBOpr.iXres = g_tFBVar.xres;
    g_tFBOpr.iYres = g_tFBVar.yres;
    g_tFBOpr.iBpp = g_tFBVar.bits_per_pixel;

    g_dwLineWidth = g_tFBVar.xres * g_tFBVar.bits_per_pixel / 8;
    g_dwPixelWidth = g_tFBVar.bits_per_pixel / 8;
    return 0;
}

static int FBShowPixel(int iX, int iY, unsigned int dwColor)
{
    if ((iX >= g_tFBOpr.iXres) || (iY >= g_tFBOpr.iYres))
    {
        DBG_PRINTF("chen FBShowPixel out of memory\n");
        return -1;
    }

    unsigned char *p = g_pucFBMem + iX * g_dwPixelWidth + iY * g_dwLineWidth;
    unsigned short *pwFB16bpp = (unsigned short *)p;
    unsigned int *pwFB32bpp = (unsigned int *)p;

    int iRed;
    int iGreen;
    int iBlue;
    switch (g_tFBOpr.iBpp)
    {
    case 8:
        *p = (unsigned char)dwColor;
        break;
    case 16:
        iRed = (dwColor >> (16 + 3)) & 0x1f;
        iGreen = (dwColor >> 10) & 0x3f;
        iBlue = dwColor & 0x1f;
        unsigned short clor = iRed << 11 | iGreen << 5 | iBlue;
        *pwFB16bpp = clor;
        break;
    case 32:
        *pwFB32bpp = dwColor;
        break;
    default:

        DBG_PRINTF("chen can't support %d bpp\n", g_tFBVar.bits_per_pixel);
        return -1;
    }
    return 0;
}

// static int FBCleanScreen(unsigned int dwBackColor)
// {
// 	unsigned char *pucFB;
// 	unsigned short *pwFB16bpp;
// 	unsigned int *pdwFB32bpp;
// 	unsigned short wColor16bpp; /* 565 */
// 	int iRed;
// 	int iGreen;
// 	int iBlue;
// 	int i = 0;

// 	pucFB      = g_pucFBMem;
// 	pwFB16bpp  = (unsigned short *)pucFB;
// 	pdwFB32bpp = (unsigned int *)pucFB;

// 	switch (g_tFBVar.bits_per_pixel)
// 	{
// 		case 8:
// 		{
// 			memset(g_pucFBMem, dwBackColor, g_dwScreenSize);
// 			break;
// 		}
// 		case 16:
// 		{
// 			iRed   = (dwBackColor >> (16+3)) & 0x1f;
// 			iGreen = (dwBackColor >> (8+2)) & 0x3f;
// 			iBlue  = (dwBackColor >> 3) & 0x1f;
// 			wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
// 			while (i < g_dwScreenSize)
// 			{
// 				*pwFB16bpp	= wColor16bpp;
// 				pwFB16bpp++;
// 				i += 2;
// 			}
// 			break;
// 		}
// 		case 32:
// 		{
// 			while (i < g_dwScreenSize)
// 			{
// 				*pdwFB32bpp	= dwBackColor;
// 				pdwFB32bpp++;
// 				i += 4;
// 			}
// 			break;
// 		}
// 		default :
// 		{
// 			DBG_PRINTF("can't support %d bpp\n", g_tFBVar.bits_per_pixel);
// 			return -1;
// 		}
// 	}

// 	return 0;
// }

static int FBCleanScreen(unsigned int dwBackColor)
{
    unsigned char *p = g_pucFBMem;
    unsigned short *pwFB16bpp = (unsigned short *)p;
    unsigned int *pwFB32bpp = (unsigned int *)p;

    int iRed;
    int iGreen;
    int iBlue;
    unsigned char *end = p + g_dwScreenSize;

    while (p < end)
    {
        switch (g_tFBOpr.iBpp)
        {
        case 8:
            *p = (unsigned char)dwBackColor;
            p++;
            break;
        case 16:
            iRed = (dwBackColor >> (16 + 3)) & 0x1f;
            iGreen = (dwBackColor >> 10) & 0x3f;
            iBlue = dwBackColor & 0x1f;
            unsigned short clor = iRed << 11 | iGreen << 5 | iBlue;
            *pwFB16bpp = clor;
            pwFB16bpp ++;
            p =(unsigned char*)pwFB16bpp;
            break;
        case 32:
            *pwFB32bpp = dwBackColor;
            pwFB32bpp++;
            p = (unsigned char*)pwFB32bpp;
            break;
        default:
            DBG_PRINTF("chen can't support %d bpp\n", g_tFBVar.bits_per_pixel);
            return -1;
        }
    }
    return 0;
}

int FBInit(void)
{
    return RegisterDispOpr(&g_tFBOpr);
}